/**
  *******************************************************************************
  * @file    system_MR88F002.c
  * @author  ngms
  * @version 1.0.9
  * @date    2021-08-04
  * @brief   
  *******************************************************************************
  * @attention
  * 
  * 
  *******************************************************************************
  */

#include <stdint.h>
#include "MR88Fx02.h"

//#define FPGA

/*----------------------------------------------------------------------------
  DEFINES
 *----------------------------------------------------------------------------*/


uint32_t HXTAL1Frequency = (8000000UL);
uint32_t HXTAL2Frequency = (8000000UL);
uint32_t HSRCFrequency   = (48000000UL);
uint32_t SystemCoreClock = (8000000UL);

/**
 * System core clock selection
 *
 * @param  cc : Core clock selection 
 * @return none
 *
 * @brief  Setup the core clock and update the SystemCoreClock variable.
 * 
 * @note The function call should be followed by a call to SystemCoreClockUpdate().
 */
void SystemCoreClockSelect(eCoreClk cc)
{   
    SYSCTRL->APBBCKCON |= (1UL<<SYSCTRL_APBBCKCON_ANACTLBCKE_Pos); 
    
    switch (cc) 
    {
        case CoreClk_HSRC8M:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2;   
            
            //enable
            SYSCTRL->HSRCCON_b.HSRCEN = 1;
            
            // set trim bits
            //SYSCTRL->HSRCTRIM = HSRC8M_TRIM;
            SYSCTRL->SYSCLKDIV_b.AHBPRES = 5;
            // switch to HSRC
            SYSCTRL->SYSCLKSEL_b.SYSCLKSEL = 0; 
            // default to 0 wait, ACR does not need the FLS bus clock
            FLSCTRL->ACR = 0;        
            break;
        }
        
        case CoreClk_HSRC16M:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2; 
            
            //enable
            SYSCTRL->HSRCCON_b.HSRCEN = 1;
            
            // set trim bits
            //SYSCTRL->HSRCTRIM = HSRC8M_TRIM;
            SYSCTRL->SYSCLKDIV_b.AHBPRES = 2;
            // switch to HSRC
            SYSCTRL->SYSCLKSEL_b.SYSCLKSEL = 0; 
            // default to 0 wait, ACR does not need the FLS bus clock             
            FLSCTRL->ACR = 0;
            break;
        }
        
        case CoreClk_HSRC24M:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2; 
            
            //enable
            SYSCTRL->HSRCCON_b.HSRCEN = 1;
            
            // set trim bits
            //SYSCTRL->HSRCTRIM = HSRC8M_TRIM;
            SYSCTRL->SYSCLKDIV_b.AHBPRES = 1;
            // switch to HSRC
            SYSCTRL->SYSCLKSEL_b.SYSCLKSEL = 0; 
            // default to 0 wait, ACR does not need the FLS bus clock             
            FLSCTRL->ACR = 0;
            break;
        }
        
        case CoreClk_HSRC48M:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2;   
            
            //enable
            SYSCTRL->HSRCCON_b.HSRCEN = 1;
            
            // set trim bits
            //SYSCTRL->HSRCTRIM = HSRC8M_TRIM;
            SYSCTRL->SYSCLKDIV_b.AHBPRES = 0;
            // switch to HSRC
            SYSCTRL->SYSCLKSEL_b.SYSCLKSEL = 0; 
            // default to 1 wait, ACR does not need the FLS bus clock             
            FLSCTRL->ACR = 1;  
            break;
        }
        
        case CoreClk_LSRC:
        {
            // switch to LSRC
            SYSCTRL->SYSCLKSEL_b.LSCLKSEL = 0;  
            SYSCTRL->SYSCLKSEL_b.SYSCLKSEL = 2;  
            FLSCTRL->ACR = 0;          
            break;
        }
        
        case CoreClk_XTAL1L:
        {
            // enable clock stop detection
            ANACTL->FDETIE = (1<<ANACTL_FDETIE_XTAL1LDEN_Pos);
            // enable XTAL
            SYSCTRL->HXTALCON_b.XTAL1LEN = 1;
            
            // switch to HXTAL
            SYSCTRL->SYSCLKSEL_b.LSCLKSEL = 1;  
            SYSCTRL->SYSCLKSEL_b.SYSCLKSEL = 2;  
            FLSCTRL->ACR = 0;  
            break;
        }
        
        case CoreClk_XTAL1H:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2; 
            // enable clock stop detection
            ANACTL->FDETIE = (1<<ANACTL_FDETIE_XTAL1HDEN_Pos);
            // enable XTAL
            SYSCTRL->HXTALCON_b.XTAL1HEN = 1;
            
            // switch to HXTAL
            SYSCTRL->SYSCLKSEL = 0x1;   
            // default to 0 wait, ACR does not need the FLS bus clock 
            FLSCTRL->ACR = 0;  
            break;
        }
        
        case CoreClk_XTAL1H_32MHz:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2; 
            // enable clock stop detection
            ANACTL->FDETIE = (1<<ANACTL_FDETIE_XTAL1HDEN_Pos);
            // enable XTAL
            SYSCTRL->HXTALCON_b.XTAL1HEN = 1;
            
            // switch to HXTAL
            SYSCTRL->SYSCLKSEL = 0x1;   
            // default to 1 wait, ACR does not need the FLS bus clock 
            FLSCTRL->ACR = 1;  
            break;
        }
        
        case CoreClk_XTAL2:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2; 
            // enable clock stop detection
            ANACTL->FDETIE = (1<<ANACTL_FDETIE_XTAL2HDEN_Pos);
            // enable XTAL
            SYSCTRL->HXTALCON_b.XTAL2HEN = 1;
            
            // switch to HXTAL
            SYSCTRL->SYSCLKSEL = 0x3;   
            // default to 0 wait, ACR does not need the FLS bus clock 
            FLSCTRL->ACR = 0;  
            break;
        }
        
        case CoreClk_XTAL2_32MHz:
        {
            // precautionary step, in case trim values differ to much
            FLSCTRL->ACR = 2; 
            // enable clock stop detection
            ANACTL->FDETIE = (1<<ANACTL_FDETIE_XTAL2HDEN_Pos);
            // enable XTAL
            SYSCTRL->HXTALCON_b.XTAL2HEN = 1;
            
            // switch to HXTAL
            SYSCTRL->SYSCLKSEL = 0x3;   
            // default to 1 wait, ACR does not need the FLS bus clock 
            FLSCTRL->ACR = 1;  
            break;
        }
        
        default:
        {
            // keep the core clock setting
            break;
        }
    }
	
	//#ifdef FPGA
	//FLSCTRL->ACR = 2;
	//#else
	//FLSCTRL->ACR = 1;
	//#endif
	
}

/**
 * Update the HXTAL frequency
 *
 * @param  hxtal_freq : user specified xtal frequency
 * @return none
 *
 * @brief  Update the core clock frequency, based on SYSCLKDIV.
 */
void SystemUpdateHXTAL1Frequency(uint32_t hxtal_freq)
{
    HXTAL1Frequency = hxtal_freq;
}

/**
 * Update the HXTAL frequency
 *
 * @param  hxtal_freq : user specified xtal frequency
 * @return none
 *
 * @brief  Update the core clock frequency, based on SYSCLKDIV.
 */
void SystemUpdateHXTAL2Frequency(uint32_t hxtal_freq)
{
    HXTAL2Frequency = hxtal_freq;
}

/**
 * Update SystemCoreClock variable
 *
 * @param  none
 * @return none
 *
 * @brief  Updates the SystemCoreClock with current core Clock
 *         retrieved from cpu registers.
 */
void SystemCoreClockUpdate(void)          
{
    //uint32_t FSELTab[4] = {8000000UL, 16000000UL, 24000000UL, 48000000UL};
    //uint32_t AHBPresTab[4] = {0, 0, 0, 0, 1, 2, 3, 4};
    
    uint32_t clksel;
    //uint32_t lsclksel;
    uint32_t ahbpres;

    // determine clock source
    clksel   = (SYSCTRL->SYSCLKSEL & SYSCTRL_SYSCLKSEL_SYSCLKSEL_Msk)>>SYSCTRL_SYSCLKSEL_SYSCLKSEL_Pos;
    //lsclksel = (SYSCTRL->SYSCLKSEL & SYSCTRL_SYSCLKSEL_LSCLKSEL_Msk)>>SYSCTRL_SYSCLKSEL_LSCLKSEL_Pos;
    if (0x0 == clksel)
    {
        SystemCoreClock = HSRCFrequency;
    }
    else if (0x1 == clksel)
    {
        SystemCoreClock = HXTAL1Frequency;
    }
    else if (0x2 == clksel)
    {
        SystemCoreClock = 32000UL;  // LSRC or XTAL1L
    }
    else if (0x3 == clksel)
    {
        SystemCoreClock = HXTAL2Frequency;
    }
    
    ahbpres = (SYSCTRL->SYSCLKDIV & SYSCTRL_SYSCLKDIV_AHBPRES_Msk)>>SYSCTRL_SYSCLKDIV_AHBPRES_Pos;

    //SystemCoreClock = SystemCoreClock>>(ahbpres);  // divider         
	SystemCoreClock = SystemCoreClock/(ahbpres+1);  // divider         
}

/**
 * Initialize the system
 *
 * @param  none
 * @return none
 *
 * @brief  Setup the microcontroller system.
 *         Initialize the System.
 */
void SystemInit(void)
{
    SystemCoreClockSelect(CoreClk_HSRC8M);
    SystemCoreClockUpdate();
	
//	#warning "Add delay to allow time for debug entry in case of SWD remap" 
//	uint32_t d = 1000000;
//	while (d--);
}
